
#include "stm32f1xx.h"
#include "usbd_common.h"
#include "usbd_composite.h"
#include "usbd_cdc_implement.h"
#include "usbd_cdc_implement.h"
#include "usbprint.h"


/*
    USB 设备说明：
    2个接口 : 
        联合接口--CDC_Control & CDC_Data
            -- 端点 3 Bulk In&Out 32x2
            -- 端点 4 Int In 8
        MSC 接口
            -- 端点 1 Double Bulk In 64
            -- 端点 2 Double Bulk Out 64
*/
/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN const uint8_t USBD_COMP_CfgFSDesc[USBD_COMP_CONFIG_DESC_SIZ] __ALIGN_END =
{
    /*Configuration Descriptor*/
    0x09,   /* bLength: Configuration Descriptor size */
    USB_DESC_TYPE_CONFIGURATION,        /* bDescriptorType: Configuration */
    USBD_COMP_CONFIG_DESC_SIZ,      /* wTotalLength:no of returned bytes */
    0x00,
    0x03,   /* bNumInterfaces: 3 interface : CDC_CTRL + CDC_DATA + MSC*/
    0x01,   /* bConfigurationValue: Configuration value */
    0x00,   /* iConfiguration: Index of string descriptor describing the configuration */
    0x80,   /* bmAttributes: bus powered */
    0x32,   /* MaxPower 100 mA */

    /*---------------------------------------------------------------------------*/
    /* USB_INTERFACE_ASSOCIATION_DESCRIPTOR */
    0x08,               //长度为8
    0x0B,               //USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE，值为0x0b
    0x00,               //第一个接口编号
    0x02,               //接口总数量
    0x02,
    0x02,
    0x00,
    0x00,               //字符串描述符索引

    /*Interface Descriptor */
    0x09,   /* bLength: Interface Descriptor size */
    USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: Interface */
    /* Interface descriptor type */
    0x00,   /* bInterfaceNumber: Number of Interface */
    0x00,   /* bAlternateSetting: Alternate setting */
    0x01,   /* bNumEndpoints: One endpoints used */
    0x02,   /* bInterfaceClass: Communication Interface Class */
    0x02,   /* bInterfaceSubClass: Abstract Control Model */
    0x01,   /* bInterfaceProtocol: Common AT commands */
    0x00,   /* iInterface: */

    /*Header Functional Descriptor*/
    0x05,   /* bLength: Endpoint Descriptor size */
    0x24,   /* bDescriptorType: CS_INTERFACE */
    0x00,   /* bDescriptorSubtype: Header Func Desc */
    0x10,   /* bcdCDC: spec release number */
    0x01,

    /*Call Management Functional Descriptor*/
    0x05,   /* bFunctionLength */
    0x24,   /* bDescriptorType: CS_INTERFACE */
    0x01,   /* bDescriptorSubtype: Call Management Func Desc */
    0x00,   /* bmCapabilities: D0+D1 */
    0x01,   /* bDataInterface: 1 */

    /*ACM Functional Descriptor*/
    0x04,   /* bFunctionLength */
    0x24,   /* bDescriptorType: CS_INTERFACE */
    0x02,   /* bDescriptorSubtype: Abstract Control Management desc */
    0x02,   /* bmCapabilities */

    /*Union Functional Descriptor*/
    0x05,   /* bFunctionLength */
    0x24,   /* bDescriptorType: CS_INTERFACE */
    0x06,   /* bDescriptorSubtype: Union func desc */
    0x00,   /* bMasterInterface: Communication class interface */
    0x01,   /* bSlaveInterface0: Data Class Interface */

    /*Endpoint 2 Descriptor*/
    0x07,                             /* bLength: Endpoint Descriptor size */
    USB_DESC_TYPE_ENDPOINT,           /* bDescriptorType: Endpoint */
    CDC_CMD_EP,                       /* bEndpointAddress */
    0x03,                             /* bmAttributes: Interrupt */
    LOBYTE(CDC_CMD_PACKET_SIZE),      /* wMaxPacketSize: */
    HIBYTE(CDC_CMD_PACKET_SIZE),
    CDC_FS_BINTERVAL,                 /* bInterval: */
    /*---------------------------------------------------------------------------*/

    /*Data class interface descriptor*/
    0x09,                     /* bLength: Endpoint Descriptor size */
    USB_DESC_TYPE_INTERFACE,  /* bDescriptorType: */
    0x01,                     /* bInterfaceNumber: Number of Interface */
    0x00,                     /* bAlternateSetting: Alternate setting */
    0x02,                     /* bNumEndpoints: Two endpoints used */
    0x0A,                     /* bInterfaceClass: CDC */
    0x00,                     /* bInterfaceSubClass: */
    0x00,                     /* bInterfaceProtocol: */
    0x00,                     /* iInterface: */

    /*Endpoint OUT Descriptor*/
    0x07,   /* bLength: Endpoint Descriptor size */
    USB_DESC_TYPE_ENDPOINT,               /* bDescriptorType: Endpoint */
    CDC_OUT_EP,                           /* bEndpointAddress */
    0x02,                                 /* bmAttributes: Bulk */
    LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
    HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
    0x00,                                 /* bInterval: ignore for Bulk transfer */

    /*Endpoint IN Descriptor*/
    0x07,                                 /* bLength: Endpoint Descriptor size */
    USB_DESC_TYPE_ENDPOINT,               /* bDescriptorType: Endpoint */
    CDC_IN_EP,                            /* bEndpointAddress */
    0x02,                                 /* bmAttributes: Bulk */
    LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),  /* wMaxPacketSize: */
    HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
    0x00,                                 /* bInterval: ignore for Bulk transfer */
    /*---------------------------------------------------------------------------*/

    /********************  Mass Storage interface ********************/
    0x09,     /* bLength: Interface Descriptor size */
    0x04,     /* bDescriptorType: */
    0x02,     /* bInterfaceNumber: Number of Interface */
    0x00,     /* bAlternateSetting: Alternate setting */
    0x02,     /* bNumEndpoints*/
    0x08,     /* bInterfaceClass: MSC Class */
    0x06,     /* bInterfaceSubClass : SCSI transparent*/
    0x50,     /* nInterfaceProtocol */
    0x05,     /* iInterface: */
    /********************  Mass Storage Endpoints ********************/
    0x07,         /*Endpoint descriptor length = 7*/
    0x05,         /*Endpoint descriptor type */
    MSC_IN_EP,    /*Endpoint address (IN, address 1) */
    0x02,         /*Bulk endpoint type */
    LOBYTE(MSC_MAX_FS_PACKET),
    HIBYTE(MSC_MAX_FS_PACKET),
    0x00,         /*Polling interval in milliseconds */

    0x07,         /*Endpoint descriptor length = 7 */
    0x05,         /*Endpoint descriptor type */
    MSC_OUT_EP,   /*Endpoint address (OUT, address 1) */
    0x02,         /*Bulk endpoint type */
    LOBYTE(MSC_MAX_FS_PACKET),
    HIBYTE(MSC_MAX_FS_PACKET),
    0x00          /*Polling interval in milliseconds*/
};

static uint8_t USBD_COMP_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
static uint8_t USBD_COMP_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
static uint8_t  USBD_COMP_Setup(USBD_HandleTypeDef *pdev,
    USBD_SetupReqTypedef *req);
static uint8_t  *USBD_COMP_GetFSCfgDesc(uint16_t *length);
static uint8_t USBD_COMP_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum);
static uint8_t USBD_COMP_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum);

const USBD_ClassTypeDef USBD_COMP =
{
    USBD_COMP_Init,
    USBD_COMP_DeInit,
    USBD_COMP_Setup,
    NULL, /*EP0_TxSent*/
    CDC_Implement_EP0_RxReady, /*EP0_RxReady*/
    USBD_COMP_DataIn,
    USBD_COMP_DataOut,
#ifdef USB_PRINT
    USBPrint_SOF_IRQHandler,
#else
    NULL,
#endif
    NULL,
    NULL,
    NULL,// USBD_COMP_GetHSCfgDesc,
    USBD_COMP_GetFSCfgDesc,
    NULL,// USBD_COMP_GetOtherSpeedCfgDesc,
    NULL,// USBD_COMP_GetDeviceQualifierDescriptor,
};

#define MSC_EP_OPEN()                                                           \
do {                                                                            \
    if (pdev->dev_speed == USBD_SPEED_HIGH)                                     \
    {                                                                           \
    /* Open EP OUT */                                                           \
    USBD_LL_OpenEP(pdev, MSC_OUT_EP, USBD_EP_TYPE_BULK, MSC_MAX_HS_PACKET);     \
    pdev->ep_out[MSC_OUT_EP & 0xFU].is_used = 1U;                               \
                                                                                \
    /* Open EP IN */                                                            \
    USBD_LL_OpenEP(pdev, MSC_IN_EP, USBD_EP_TYPE_BULK, MSC_MAX_HS_PACKET);      \
    pdev->ep_in[MSC_IN_EP & 0xFU].is_used = 1U;                                 \
    }                                                                           \
    else                                                                        \
    {                                                                           \
    /* Open EP OUT */                                                           \
    USBD_LL_OpenEP(pdev, MSC_OUT_EP, USBD_EP_TYPE_BULK, MSC_MAX_FS_PACKET);     \
    pdev->ep_out[MSC_OUT_EP & 0xFU].is_used = 1U;                               \
                                                                                \
    /* Open EP IN */                                                            \
    USBD_LL_OpenEP(pdev, MSC_IN_EP, USBD_EP_TYPE_BULK, MSC_MAX_FS_PACKET);      \
    pdev->ep_in[MSC_IN_EP & 0xFU].is_used = 1U;                                 \
    }                                                                           \
} while (0)

#define CDC_EP_OPEN()                                               \
do {                                                                \
    if (pdev->dev_speed == USBD_SPEED_HIGH)                         \
    {                                                               \
        /* Open EP IN */                                            \
        USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK,          \
                        CDC_DATA_HS_IN_PACKET_SIZE);                \
                                                                    \
        pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;                 \
                                                                    \
        /* Open EP OUT */                                           \
        USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK,         \
                        CDC_DATA_HS_OUT_PACKET_SIZE);               \
                                                                    \
        pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;               \
    }                                                               \
    else                                                            \
    {                                                               \
        /* Open EP IN */                                            \
        USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK,          \
                        CDC_DATA_FS_IN_PACKET_SIZE);                \
                                                                    \
        pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;                 \
                                                                    \
        /* Open EP OUT */                                           \
        USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK,         \
                        CDC_DATA_FS_OUT_PACKET_SIZE);               \
                                                                    \
        pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;               \
    }                                                               \
    /* Open Command IN EP */                                        \
    USBD_LL_OpenEP(pdev, CDC_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);\
    pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 1U;                    \
} while(0)


/**
  * @brief  USBD_Composite_Init
  *         Initialize  the COM configuration
  * @param  pdev: device instance
  * @param  cfgidx: configuration index
  * @retval status
  */
static uint8_t USBD_COMP_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
    uint8_t ret = 0U;

    MSC_EP_OPEN();
    CDC_Implement_Endpoint_Open(pdev);
    USBD_COM_PreInit(pdev);

    /* 使用读卡器模式时，堆全部用来作MSC传输 */
    // HeapInit();

    // USBD_GETHANDLE_CDC(pdev) = (USBD_CDC_HandleTypeDef*)USBD_malloc(sizeof(USBD_CDC_HandleTypeDef));
    USBD_GETHANDLE_MSC(pdev) = (USBD_MSC_BOT_HandleTypeDef*)USBD_malloc(sizeof(USBD_MSC_BOT_HandleTypeDef));
    USBD_GETHANDLE_CDC(pdev) = &CDCHandler;
    // USBD_GETHANDLE_MSC(pdev) = heap_pointer();

    /* Init the BOT layer */
    MSC_BOT_Init(pdev);

    if (pdev->pClassData == NULL)
    {
      ret = 1U;
    }
    else
    {
       USBD_CDC_HandleTypeDef *hcdc = USBD_GETHANDLE_CDC(pdev);

      /* Init Xfer states */
      hcdc->TxState = 0U;
      hcdc->RxState = 0U;
  
      if (pdev->dev_speed == USBD_SPEED_HIGH)
      {
        /* Prepare Out endpoint to receive next packet */
        USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer,
                               CDC_DATA_HS_OUT_PACKET_SIZE);
      }
      else
      {
        /* Prepare Out endpoint to receive next packet */
        USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer,
                               CDC_DATA_FS_OUT_PACKET_SIZE);
      }
    }
    return ret;
}

/**
  * @brief  USBD_Composite_DeInit
  *         DeInitilaize  the mass storage configuration
  * @param  pdev: device instance
  * @param  cfgidx: configuration index
  * @retval ret
  */
static uint8_t USBD_COMP_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
    USBD_MSC_BOT_HandleTypeDef *hmsc = USBD_GETHANDLE_MSC(pdev);
    USBD_CDC_HandleTypeDef *hcdc = USBD_GETHANDLE_CDC(pdev);

    if (hmsc != NULL)
    {
        USBD_free((void*)hmsc);
        USBD_GETHANDLE_MSC(pdev) = NULL;
    }
    else
    {
        return USBD_FAIL;
    }
    if (hcdc != NULL)
    {
        // USBD_free((void*)hcdc);
        USBD_GETHANDLE_CDC(pdev) = NULL;
    }
    else
    {
        return USBD_FAIL;
    }

    return USBD_COM_NextDeInit(pdev);
}

/**
  * @brief  USBD_COMP_Setup
  *         Handle the CDC specific requests
  * @param  pdev: instance
  * @param  req: usb requests
  * @retval status
  */
static uint8_t  USBD_COMP_Setup(USBD_HandleTypeDef *pdev,
    USBD_SetupReqTypedef *req)
{
    uint8_t ret = USBD_OK;
    uint16_t status_info = 0U;
    USBD_MSC_BOT_HandleTypeDef *hmsc = USBD_GETHANDLE_MSC(pdev);
  
    switch (req->bmRequest & USB_REQ_TYPE_MASK)
    {
        /* Class request */
        case USB_REQ_TYPE_CLASS:
        switch (req->wIndex)
        {
            case USBD_INTERFACE_NUM_CDC_COMMAND:
            CDC_Implement_Setup_Class_Interface(pdev, req);
            break;

            case USBD_INTERFACE_NUM_MSC:
            {
            switch (req->bRequest)
            {
                case BOT_GET_MAX_LUN:
                if ((req->wValue  == 0U) && (req->wLength == 1U) &&
                    ((req->bmRequest & 0x80U) == 0x80U))
                {
                    hmsc->max_lun = (uint32_t)(USBD_GETITF_MSC(pdev)->GetMaxLun());
                    USBD_CtlSendData(pdev, (uint8_t *)(void *)&hmsc->max_lun, 1U);
                }
                else
                {
                    USBD_CtlError(pdev, req);
                    ret = USBD_FAIL;
                }
                break;
    
                case BOT_RESET :
                if ((req->wValue  == 0U) && (req->wLength == 0U) &&
                    ((req->bmRequest & 0x80U) != 0x80U))
                {
                    MSC_BOT_Reset(pdev);
                }
                else
                {
                    USBD_CtlError(pdev, req);
                    ret = USBD_FAIL;
                }
                break;
    
                default:
                USBD_CtlError(pdev, req);
                ret = USBD_FAIL;
                break;
            }
        }
        break;
        
        default:
            break;
        }
        break;


      /* Interface & Endpoint request */
      case USB_REQ_TYPE_STANDARD:
        switch (req->bRequest)
        {
          case USB_REQ_GET_STATUS:
            if (pdev->dev_state == USBD_STATE_CONFIGURED)
            {
              USBD_CtlSendData(pdev, (uint8_t *)(void *)&status_info, 2U);
            }
            else
            {
              USBD_CtlError(pdev, req);
              ret = USBD_FAIL;
            }
            break;
  
          case USB_REQ_GET_INTERFACE:
            if (pdev->dev_state == USBD_STATE_CONFIGURED)
            {
                if (req->wIndex == USBD_INTERFACE_NUM_MSC)
                {
                    USBD_CtlSendData(pdev, (uint8_t *)(void *)&hmsc->interface, 1U);
                }
            }
            else
            {
              USBD_CtlError(pdev, req);
              ret = USBD_FAIL;
            }
            break;
  
          case USB_REQ_SET_INTERFACE:
            if (pdev->dev_state == USBD_STATE_CONFIGURED)
            {
                if (req->wIndex == USBD_INTERFACE_NUM_MSC)
                {
                    hmsc->interface = (uint8_t)(req->wValue);
                }
            }
            else
            {
              USBD_CtlError(pdev, req);
              ret = USBD_FAIL;
            }
            break;
  
          case USB_REQ_CLEAR_FEATURE:
  
            /* Flush the FIFO and Clear the stall status */
            USBD_LL_FlushEP(pdev, (uint8_t)req->wIndex);
  
            /* Reactivate the EP */
            USBD_LL_CloseEP(pdev, (uint8_t)req->wIndex);
            if ((((uint8_t)req->wIndex) & 0x80U) == 0x80U)
            {
              pdev->ep_in[(uint8_t)req->wIndex & 0xFU].is_used = 0U;
              if (pdev->dev_speed == USBD_SPEED_HIGH)
              {
                /* Open EP IN */
                USBD_LL_OpenEP(pdev, MSC_IN_EP, USBD_EP_TYPE_BULK,
                               MSC_MAX_HS_PACKET);
              }
              else
              {
                /* Open EP IN */
                USBD_LL_OpenEP(pdev, MSC_IN_EP, USBD_EP_TYPE_BULK,
                               MSC_MAX_FS_PACKET);
              }
              pdev->ep_in[MSC_IN_EP & 0xFU].is_used = 1U;
            }
            else
            {
              pdev->ep_out[(uint8_t)req->wIndex & 0xFU].is_used = 0U;
              if (pdev->dev_speed == USBD_SPEED_HIGH)
              {
                /* Open EP OUT */
                USBD_LL_OpenEP(pdev, MSC_OUT_EP, USBD_EP_TYPE_BULK,
                               MSC_MAX_HS_PACKET);
              }
              else
              {
                /* Open EP OUT */
                USBD_LL_OpenEP(pdev, MSC_OUT_EP, USBD_EP_TYPE_BULK,
                               MSC_MAX_FS_PACKET);
              }
              pdev->ep_out[MSC_OUT_EP & 0xFU].is_used = 1U;
            }
  
            /* Handle BOT error */
            MSC_BOT_CplClrFeature(pdev, (uint8_t)req->wIndex);
            break;
  
          default:
            USBD_CtlError(pdev, req);
            ret = USBD_FAIL;
            break;
        }
        break;
  
      default:
        USBD_CtlError(pdev, req);
        ret = USBD_FAIL;
        break;
    }
  
    return ret;
}

/**
* @brief  USBD_COMP_RegisterInterface
  * @param  pdev: device instance
  * @param  fops: CD  Interface callback
  * @retval status
  */
static uint8_t  *USBD_COMP_GetFSCfgDesc(uint16_t *length)
{
    *length = sizeof(USBD_COMP_CfgFSDesc);
    return (uint8_t*)USBD_COMP_CfgFSDesc;
}


/**
* @brief  USBD_COMP_DataIn
*         handle data IN Stage
* @param  pdev: device instance
* @param  epnum: endpoint index
* @retval status
*/
uint8_t USBD_COMP_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
    switch (epnum)
    {
    case MSC_IN_EP & EP_ADDR_MSK:
        MSC_BOT_DataIn(pdev, epnum);
        break;
    case CDC_IN_EP & EP_ADDR_MSK:
        CDC_Implement_DataIn(pdev, epnum);
        break;
    default:
        return USBD_FAIL;
    }

  return USBD_OK;
}

/**
* @brief  USBD_COMP_DataOut
*         handle data OUT Stage
* @param  pdev: device instance
* @param  epnum: endpoint index
* @retval status
*/
uint8_t USBD_COMP_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
    switch (epnum)
    {
    case MSC_OUT_EP & EP_ADDR_MSK:
        MSC_BOT_DataOut(pdev, epnum);
        break;
    case CDC_OUT_EP & EP_ADDR_MSK:
        return CDC_Implement_DataOut(pdev, epnum);

    default:
        return USBD_FAIL;
    }
  return USBD_OK;
}

